package impl

import (
	"context"
	"fmt"
	"sync"

	"hundsun.com/hsl/hschain/blockchain/impl/pubsub"

	"hundsun.com/hsl/hschain/common/types"
	commonproto "hundsun.com/hsl/hschain/protos/common"
	"hundsun.com/hsl/hschain/store/mq"

	"hundsun.com/hsl/hschain/module"
	"hundsun.com/hsl/hschain/module/registry"
)

// BlockChainModule blockchain模块结构
type BlockChainModule struct {
	module.Adapter
	shutdownWg *sync.WaitGroup
	instance   *BlockChain
}

// NewModule 新建一个模块
func NewModule() module.Module {
	m := &BlockChainModule{}

	moduleStartupFunc := func(startedInterestedModules map[string][]*registry.ModRegKV) error {
		log := m.Log
		m.shutdownWg = new(sync.WaitGroup)
		bcCtx, bcCtxCancelFun := context.WithCancel(m.RootCtx)
		bc := NewBlockChain(bcCtx, m.Config, bcCtxCancelFun, m.Log)
		m.instance = bc

		//先加载一部分block信息到cache中
		curheight := bc.GetBlockHeight()
		if bc.enablePreCache {
			bc.InitCache(curheight)
		}

		// load genesis system param
		err := bc.InitSysParam()
		if err != nil {
			panic(err.Error())
		}

		// load genesis block
		err = bc.InitGenesisBlock()
		if err != nil {
			panic(err.Error())
		}

		// subscribe system param
		err = bc.subMsg(&commonproto.Router{
			MainTopic: types.BlockChainModule,
			SubTopic:  mq.ModeWorker.String(),
			EventType: pubsub.SubSysParam,
		})
		if err != nil {
			panic(err.Error())
		}

		go bc.ProcWorkerMsg()
		go bc.ProcTopicMsg()

		// 同步block
		m.shutdownWg.Add(1)
		go func() {
			defer m.shutdownWg.Done()
			bc.SynRoutine()
		}()

		// 处理需要持久化的区块
		m.shutdownWg.Add(1)
		go func() {
			defer m.shutdownWg.Done()
			bc.ProcCheckedCacheBlock()
		}()

		//boss
		//启动worker连接状态监控
		m.shutdownWg.Add(1)
		go func() {
			defer func() {
				m.shutdownWg.Done()
			}()
			bc.workerMonitor()
		}()

		log.Infof("Current tx recordHeight num is %d ", bc.txRecordNum)

		//从链上加载数据
		producedNum, exedNum, err := bc.Recover()
		if err != nil {
			errStr := fmt.Sprintf("Load data from chain failed %v", err)
			log.Error(errStr)
			panic(errStr)
		}

		//处理创世块
		if exedNum == -1 {
			bc.ProcessGenesisBlock()
		}

		//如果当前出块高度大于已执行区块高度,需要先执行这些区块
		if producedNum > bc.curBlockNum && bc.curBlockNum >= 0 {
			log.Infof("producedNum [%d] bigger than the exedNum [%d], execute these blocks first",
				producedNum, bc.curBlockNum)
			go bc.OnBlockAccepted(uint64(producedNum))
		}

		//启动block处理
		m.shutdownWg.Add(1)
		go func() {
			defer m.shutdownWg.Done()
			bc.ProcessNewBlocks()
		}()

		//启动交易分组处理
		m.shutdownWg.Add(1)
		go func() {
			defer m.shutdownWg.Done()
			bc.processTxGroups()
		}()

		//启动定时提交任务
		m.shutdownWg.Add(1)
		go func() {
			defer m.shutdownWg.Done()
			bc.startCommitTask()
		}()

		return nil
	}
	m.Init(module.BlockchainModuleName, nil, moduleStartupFunc)
	return m
}

// Shutdown 关闭模块
func (m *BlockChainModule) Shutdown() (module.Status, error) {
	m.Log.Infof("shutdown %s", m.Config.ModuleID)
	if m.RootCtxCancelFunc != nil {
		m.RootCtxCancelFunc()
	}

	if m.instance != nil {
		m.instance.Close()
	}

	if m.shutdownWg != nil {
		m.shutdownWg.Wait()
	}

	if m.instance.pubsubMgr != nil {
		m.instance.pubsubMgr.Close()
	}
	m.Status = module.StatusDown
	return m.Status, nil
}
